<!-- Copyright 2013 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// -->
<!DOCTYPE html>
<title>Unit Test of e2e.openpgp.Context</title>
<script src="../../../../../javascript/closure/base.js"></script>
<script src="test_js_deps-runfiles.js"></script>
<script>
  goog.require('e2e.async.Result');
  goog.require('e2e.openpgp.Context');
  goog.require('e2e.openpgp.ContextImpl');
  goog.require('e2e.openpgp.error.Error');
  goog.require('goog.crypt');
  goog.require('goog.testing.AsyncTestCase');
  goog.require('goog.testing.MockControl');
  goog.require('goog.testing.jsunit');
  goog.require('goog.testing.storage.FakeMechanism');
</script>
<!--
:public key packet:
  version 4, algo 1, created 1384458137, expires 0
  pkey[0]: [2048 bits]
  pkey[1]: [17 bits]
:user ID packet: "RSARSA KEY (RSA/RSA key generated by gpg) <rsarsa@pgpkey.example.com>"
:signature packet: algo 1, keyid 8F652698C330A72A
  version 4, created 1384458137, md5len 0, sigclass 0x13
  digest algo 2, begin of digest 8f 5a
  hashed subpkt 2 len 4 (sig created 2013-11-14)
  hashed subpkt 27 len 1 (key flags: 03)
  hashed subpkt 11 len 5 (pref-sym-algos: 9 8 7 3 2)
  hashed subpkt 21 len 5 (pref-hash-algos: 8 2 9 10 11)
  hashed subpkt 22 len 3 (pref-zip-algos: 2 3 1)
  hashed subpkt 30 len 1 (features: 01)
  hashed subpkt 23 len 1 (key server preferences: 80)
  subpkt 16 len 8 (issuer key ID 8F652698C330A72A)
  data: [2046 bits]
:public sub key packet:
  version 4, algo 1, created 1384458137, expires 0
  pkey[0]: [2048 bits]
  pkey[1]: [17 bits]
:signature packet: algo 1, keyid 8F652698C330A72A
  version 4, created 1384458137, md5len 0, sigclass 0x18
  digest algo 2, begin of digest 31 f7
  hashed subpkt 2 len 4 (sig created 2013-11-14)
  hashed subpkt 27 len 1 (key flags: 0C)
  subpkt 16 len 8 (issuer key ID 8F652698C330A72A)
  data: [2044 bits]
-->
<textarea id="publicKeyAscii">
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1.4.11 (GNU/Linux)

mQENBFKFJ5kBCADgTy3vZ27fhfiFDUSsCx5ldIHwE1GbuZCm+WIhCH3ni2CL0lKY
AUNFWRcxeODlQlvYA2ti+JALWjzlUYTpf02g3LfzBSIqdDn3JBwN6U12A0xwwSRs
KSRp8phn9yCyCTO7btbqZtGc29jrX/k6cbrBE8ln/9wlZVx8KVcpzDI4jhXjUwmM
JYvJqCHXRTe3kEGZkONfAeeIBfvs7t7Y/sv80RuXnlwMVMjEZKGRFlNQ5zqr8QQb
qh9MA7NeezIi890cQzii/+w6ta7X4/BnML4iud/aNiLYhpeZwEFqXHMwp+w8xHkk
YharW9Kgng6Vw7UE10SxeUqCjaLnlSf82Z5TABEBAAG0RVJTQVJTQSBLRVkgKFJT
QS9SU0Ega2V5IGdlbmVyYXRlZCBieSBncGcpIDxyc2Fyc2FAcGdwa2V5LmV4YW1w
bGUuY29tPokBOAQTAQIAIgUCUoUnmQIbAwYLCQgHAwIGFQgCCQoLBBYCAwECHgEC
F4AACgkQj2UmmMMwpyqPWgf+Jc4DzCCxf3S1M7zM49y7VNne9H2Fy2TCyvidhlXd
OpI4dNKjIyUmYQZTLEWcWpnxcK5YcypxDrFX69s0F5s8cd9CN2gIRglHSHpay8oj
2NTJ8DS05pmUyeKlG429zah0n1OtHsCOq/E/ibDmDLUsN2/ECh6ZNxpVIig5+sin
JpP5n2j1vO3QbGs2pggaY5CWjayN2MyjIaIWyPrq/H08LPJPV41rFdU9Dur7/Eby
cclQ6yM2fg8D+wzK2gH4zvxSIpegD7vA5+GN2RYP3iYVx7i+DK8PQdTBe/qL2WDa
Kpy7DyhL5P4WNwPo51GHP5TBNvL1z0IzqyfcVuZtRVZ64LkBDQRShSeZAQgAuR/p
QU1Laihb2ladg6ymg+Crs5ap3cdjeW+vPQ8E4uVi3oZXfpfpHLYTY6EchVMJKU9g
oZFn/z2vb67h5/vSARIXFTU5ccwzegW6lG+zPcY+QxpmkjMiLlpDN7cMu9oJE44+
I3KFhUQkkEPGB5BznnsCCr10Cwe1Tf6LDs/BvrXBPoi4/ex329yFU5hbp5OWzylJ
Ak4hcPVMiYe0YFNb4VHUGwfwWqlgHuHfKWfbN6E7F0EaZosGZALGt981iNA+P1uQ
1rzm0dMaqKwRh2IAPeCDeiIBkbFgoae8eE/4f32xpV+8J0nUYZCKZjhxZYbAJfDx
1SY0HZ6ex3MKscVgBwARAQABiQEfBBgBAgAJBQJShSeZAhsMAAoJEI9lJpjDMKcq
MfcH/As1jLNy+H62LG/kgXL4i4mhjouI4Og54FKZsvcmJHM/2JX3TsYhU0PAJ4MB
fHyNZ9BUiuFpsQupERQ9nOxkMUL5nm4xn5dBVa4ZumUcnr/kIib5gFhs6nHt5LUo
1XeiCW7bt0ac7hOTECox+ZM5YabBO+cXLfdCUQp7vDh4xfxjnX7+fviLvY32nboX
rxA+hcluYFJdl87kTatUir6vA7I7KBtlwnj2dRaM1BiFmXmbj6eHnBZ5YxCh1yfq
mcR6P4l2NfaqJ5k4olWcWTLmjlwNMsWRL26zFRcpbpmle1F5rCx0CS0XXAHLJmIe
6LOU9E37DV4E77Ali8P/7IbNyqE=
=A9tz
-----END PGP PUBLIC KEY BLOCK-----
</textarea>
<!--
// ECDSA key, ECDH subkey.
:public key packet:
  version 4, algo 19, created 1377714336, expires 0
  unknown algorithm 19
:user ID packet: "ecc real name <ecc@example.com>"
:signature packet: algo 19, keyid EAEB8A76AFE8C867
  version 4, created 1377714336, md5len 0, sigclass 0x13
  digest algo 8, begin of digest c6 d7
  hashed subpkt 2 len 4 (sig created 2013-08-28)
  hashed subpkt 27 len 1 (key flags: 03)
  hashed subpkt 11 len 5 (pref-sym-algos: 9 8 7 3 2)
  hashed subpkt 21 len 5 (pref-hash-algos: 8 2 9 10 11)
  hashed subpkt 22 len 3 (pref-zip-algos: 2 3 1)
  hashed subpkt 30 len 1 (features: 01)
  hashed subpkt 23 len 1 (key server preferences: 80)
  subpkt 16 len 8 (issuer key ID EAEB8A76AFE8C867)
  unknown algorithm 19
:public sub key packet:
  version 4, algo 18, created 1377714336, expires 0
  unknown algorithm 18
:signature packet: algo 19, keyid EAEB8A76AFE8C867
  version 4, created 1377714336, md5len 0, sigclass 0x18
  digest algo 8, begin of digest 3a 90
  hashed subpkt 2 len 4 (sig created 2013-08-28)
  hashed subpkt 27 len 1 (key flags: 0C)
  subpkt 16 len 8 (issuer key ID EAEB8A76AFE8C867)
  unknown algorithm 19
-->
<textarea id="publicKeyAsciiOther">
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v2.1.0-ecc (GNU/Linux)

mFIEUh5AoBMIKoZIzj0DAQcCAwT68182duSEJFXKa+qkBa0Vgeswnv8GP8tKYiU/
MCZd6dGTvrtf2gSjyAsVkB0V0idW7i8yW1wfh3y2AbGWDr/dtB9lY2MgcmVhbCBu
YW1lIDxlY2NAZXhhbXBsZS5jb20+iHoEExMIACIFAlIeQKACGwMGCwkIBwMCBhUI
AgkKCwQWAgMBAh4BAheAAAoJEOrrinav6MhnxtcA/iAteDFo/P5SU5XV/8/4BN9x
f28SuvwFipnjjyOmvB0eAP4kPM5LAp2EW+QIyG6+CJP1No9uWyZTdLPkTRgLtYhi
GLhWBFIeQKASCCqGSM49AwEHAgMEgk1dVpgPCM38NBNoBcvehm7mt6aUmK8mDb/M
SHo2/NlwfTh+BDCoVX5asSetzuW2RbnP6sCBwfsuLSrSWUVauwMBCAeIYQQYEwgA
CQUCUh5AoAIbDAAKCRDq64p2r+jIZzqQAQCcv0VOQFiNOM6JNdLHTqlCYxeoz09d
UP3LdgcnLED/YwD9FqcNrkok9BuXJ9+rXTSu+uqdWB7gpMO9mfk65d5IQ+s=
=xRCj
-----END PGP PUBLIC KEY BLOCK-----
</textarea>
<!--
:secret key packet:
  version 4, algo 1, created 1384458137, expires 0
  skey[0]: [2048 bits]
  skey[1]: [17 bits]
  iter+salt S2K, algo: 3, SHA1 protection, hash: 2, salt: de41e60fe1e26260
  protect count: 96
  protect IV:  4d d2 0f 11 de ec e8 f2
  encrypted stuff follows
:user ID packet: "RSARSA KEY (RSA/RSA key generated by gpg) <rsarsa@pgpkey.example.com>"
:signature packet: algo 1, keyid 8F652698C330A72A
  version 4, created 1384458137, md5len 0, sigclass 0x13
  digest algo 2, begin of digest 8f 5a
  hashed subpkt 2 len 4 (sig created 2013-11-14)
  hashed subpkt 27 len 1 (key flags: 03)
  hashed subpkt 11 len 5 (pref-sym-algos: 9 8 7 3 2)
  hashed subpkt 21 len 5 (pref-hash-algos: 8 2 9 10 11)
  hashed subpkt 22 len 3 (pref-zip-algos: 2 3 1)
  hashed subpkt 30 len 1 (features: 01)
  hashed subpkt 23 len 1 (key server preferences: 80)
  subpkt 16 len 8 (issuer key ID 8F652698C330A72A)
  data: [2046 bits]
:secret sub key packet:
  version 4, algo 1, created 1384458137, expires 0
  skey[0]: [2048 bits]
  skey[1]: [17 bits]
  iter+salt S2K, algo: 3, SHA1 protection, hash: 2, salt: de41e60fe1e26260
  protect count: 96
  protect IV:  67 2a 88 fd 02 55 80 c7
  encrypted stuff follows
:signature packet: algo 1, keyid 8F652698C330A72A
  version 4, created 1384458137, md5len 0, sigclass 0x18
  digest algo 2, begin of digest 31 f7
  hashed subpkt 2 len 4 (sig created 2013-11-14)
  hashed subpkt 27 len 1 (key flags: 0C)
  subpkt 16 len 8 (issuer key ID 8F652698C330A72A)
  data: [2044 bits]
-->
<textarea id="privateKeyAscii">
-----BEGIN PGP PRIVATE KEY BLOCK-----
Version: GnuPG v1.4.11 (GNU/Linux)

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
/+yGzcqh
=no9X
-----END PGP PRIVATE KEY BLOCK-----
</textarea>
<!--
Two keyblocks in one ASCII armor
:public key packet:
  version 4, algo 19, created 1408720454, expires 0
  pkey[0]: [72 bits] nistp256 (1.2.840.10045.3.1.7)
  pkey[1]: [515 bits]
  keyid: B2F39422E42C5EA7
:user ID packet: "<key1>"
:signature packet: algo 19, keyid B2F39422E42C5EA7
  version 4, created 1408720454, md5len 0, sigclass 0x10
  digest algo 8, begin of digest 70 7c
  critical hashed subpkt 2 len 4 (sig created 2014-08-22)
  critical hashed subpkt 16 len 8 (issuer key ID B2F39422E42C5EA7)
  data: [256 bits]
  data: [254 bits]
:public sub key packet:
  version 4, algo 18, created 1408720454, expires 0
  pkey[0]: [72 bits] nistp256 (1.2.840.10045.3.1.7)
  pkey[1]: [515 bits]
  pkey[2]: [32 bits]
  keyid: 1E2FB2A7764596A9
:signature packet: algo 19, keyid B2F39422E42C5EA7
  version 4, created 1408720454, md5len 0, sigclass 0x18
  digest algo 8, begin of digest c9 aa
  critical hashed subpkt 2 len 4 (sig created 2014-08-22)
  critical hashed subpkt 16 len 8 (issuer key ID B2F39422E42C5EA7)
  data: [255 bits]
  data: [256 bits]
:public key packet:
  version 4, algo 19, created 1408720457, expires 0
  pkey[0]: [72 bits] nistp256 (1.2.840.10045.3.1.7)
  pkey[1]: [515 bits]
  keyid: 537B85C172B7F7E2
:user ID packet: "<key2>"
:signature packet: algo 19, keyid 537B85C172B7F7E2
  version 4, created 1408720457, md5len 0, sigclass 0x10
  digest algo 8, begin of digest 72 b0
  critical hashed subpkt 2 len 4 (sig created 2014-08-22)
  critical hashed subpkt 16 len 8 (issuer key ID 537B85C172B7F7E2)
  data: [255 bits]
  data: [256 bits]
:public sub key packet:
  version 4, algo 18, created 1408720457, expires 0
  pkey[0]: [72 bits] nistp256 (1.2.840.10045.3.1.7)
  pkey[1]: [515 bits]
  pkey[2]: [32 bits]
  keyid: 739BC5340804414E
:signature packet: algo 19, keyid 537B85C172B7F7E2
  version 4, created 1408720457, md5len 0, sigclass 0x18
  digest algo 8, begin of digest ad de
  critical hashed subpkt 2 len 4 (sig created 2014-08-22)
  critical hashed subpkt 16 len 8 (issuer key ID 537B85C172B7F7E2)
  data: [256 bits]
  data: [253 bits]
-->
<textarea id="twoKeyBlocks">
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v2

mgAAAFIEU/deRhMIKoZIzj0DAQcCAwR5v5o25a5yQ7KCM5BYD7HTETMkNo046gIk
3WlqzKkmd6JruWIpk3TpuIthczhebqj3B2dFZMUHdPzJi2bP78mBtAY8a2V5MT6I
ZgQQEwgAGP8AAAAFglP3Xkb/AAAACZCy85Qi5CxepwAAcHwBANMTS2vteSXEaZc3
t0QGmBg4LLoxOJD0HEKrXwXKoUE7AP4ongJhDPLiasvl4t7RWKr8nQTnfkPtiPDD
eoacrmc8hLoAAABWBFP3XkYSCCqGSM49AwEHAgMEsGUid8gCTVUP+1Dswn6JFZ4P
UtbBbMYr5ez1kOhsnDf+8eg++pI8G6yToM2EVH2VwUhr4QL1dkkKTf78uEaAfQMB
CAeIZgQYEwgAGP8AAAAFglP3Xkb/AAAACZCy85Qi5CxepwAAyaoA/0gcF7Ev+t4j
vQhEQAJZES14kVyoctIs0SyQfDk8/tUMAQDOAEt1aDalOvYHnIKVBirJxNtPmga9
4jRFuyCzt1u5zpoAAABSBFP3XkkTCCqGSM49AwEHAgMEHPrNMdvJCViouD49LarG
mQvy7aHygnqX0/v/0Tnzn+CwvSUG/bedyQCKu1dLQaCu1uMj+Hf+zNPfAPbWToMt
9bQGPGtleTI+iGYEEBMIABj/AAAABYJT915J/wAAAAmQU3uFwXK39+IAAHKwAP9d
9ZRvsX3SNh6dy/35pcSVwtFkazHtj4Vmzg36Bq9jVAEAlB0TjALGW30gIPwO9+SP
IEjTqXTLYuShXAogMFDTY7e6AAAAVgRT915JEggqhkjOPQMBBwIDBMHOmw0FeP+w
GoJ5qR9ev7JW2Dn1oVwAxpKyVXodLZ83MLss3ZmCWF0c2QD8tf9WAYAE8d335iQt
F/8ixELMQ6oDAQgHiGYEGBMIABj/AAAABYJT915J/wAAAAmQU3uFwXK39+IAAK3e
AQCRn3zu2t/VZBIenq3blEtDgOtA2zAWbg2HmZ9naHWo3gD9HSK58DYJRkXIWYtt
Lz6REnkw4cB5lruzwwiTO7ZqHZg=
=WAJG
-----END PGP PUBLIC KEY BLOCK-----
</textarea>
<!--
Two keys in a keyring, but one has only weak signatures.

:public key packet:
  version 3, algo 1, created 1417737631, expires 0
  pkey[0]: [1024 bits]
  pkey[1]: [5 bits]
  keyid: BE8DC9C2C7A82461
:user ID packet: "abc@def.com"
:signature packet: algo 1, keyid BE8DC9C2C7A82461
  version 3, created 1417737631, md5len 5, sigclass 0x10
  digest algo 1, begin of digest 32 c1
  data: [1023 bits]
:public key packet:
  version 4, algo 1, created 1408908363, expires 0
  pkey[0]: [1024 bits]
  pkey[1]: [17 bits]
  keyid: B721BAA1B1D2E72B
:user ID packet: "rsa-master"
:signature packet: algo 1, keyid B721BAA1B1D2E72B
  version 4, created 1417548363, md5len 0, sigclass 0x13
  digest algo 2, begin of digest c1 69
  hashed subpkt 2 len 4 (sig created 2014-12-02)
  hashed subpkt 27 len 1 (key flags: 03)
  hashed subpkt 11 len 1 (pref-sym-algos: 7)
  hashed subpkt 21 len 1 (pref-hash-algos: 8)
  hashed subpkt 30 len 1 (features: 01)
  subpkt 16 len 8 (issuer key ID B721BAA1B1D2E72B)
  data: [1021 bits]
-->
<textarea id="skipKeyBlocks">
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1

mQCNA1SA9Z8AAAEEAKIXRwS5KCk6H7gi9DGAOlEiNkIxnoC6BxmIYiv6Mr3PRScj
/L0w7Q/uocZVKLUp6BK11FYWA9t5QtLW4Jr1o2nHFvaU/GpQJSy0mAIrg3wlW3Aq
GHKZfzUVjkAeOuYYy7lOCpIKTmQnM5uiiso/v5rQVTTnh2E+Qr6NycLHqCRhAAUR
tAthYmNAZGVmLmNvbYkAlQMFEFSA9Z++jcnCx6gkYQEBMsED/3EGVTBWO/jq1x3C
CyBm//Yrg98qe86NMwdmIhPx9Pnsa5TCPsXea132W4eoWLDKlzhJe65PX2/W1DIk
/za6YMeNzRkkbyvPKnZg6plKQDvfaX6LqyX752+DAFZ76EGeJI4wNWR/wh1S49Hr
7PCfaYY10Sfes4D8KMcNN9nReNbUmI0EU/o8SwEEALkvOE64xctaf8YlRDlxpoyM
xEoVE0iBT9o7wYPRyth/hqGKD8rTF2t/FCF3bmJnxdZvitElcp7UxQyVgeMwlhMI
JSj9CtHwHRiZ6CY9gp8Swh1srlsUVoZqGJGHgt4GKM9YPL6NCpFHdZhqp1COWUpe
BrefvEtJTbNDjJuG+uw9ABEBAAG0CnJzYS1tYXN0ZXKIqAQTAQIAEgUCVH4SSwIb
AwILBwIVCAIeAQAKCRC3IbqhsdLnK8FpA/0R/aQx8ebgQ3E0ouia06AyHRTY5SNn
WRELzAP3f1H+aFVrI11iBjPvc3pKJbnh68mtlDNWIi1pJBrWHNwOBg0Spj8Jbt8i
K8Tr/E8Wn09C1vn+xJYNMUh4tjElz2DimuS4AefmevwVtuJH1FcSgdxcw9iGbsap
eGC9JuVC0VK58w==
=KNea
-----END PGP PUBLIC KEY BLOCK-----
</textarea>

<!--
echo "hello bz2 world" > file.txt
gpg --armor --compress-algo bzip2 -c file.txt

:symkey enc packet: version 4, cipher 3, s2k 3, hash 2
  salt 1dea6a58eb6dfdea, count 65536 (96)
gpg: CAST5 encrypted data
:encrypted data packet:
  length: 94
gpg: encrypted with 1 passphrase
:compressed packet: algo=3
:literal data packet:
  mode b (62), created 1452906719, name="file.txt",
  raw data: 16 bytes
-->
<textarea id="symmetricKeyBzip2">
-----BEGIN PGP MESSAGE-----
Version: GnuPG v1

jA0EAwMCHepqWOtt/epgyV5U5XfyScWgyppsiIm45xEgw8PKjwnjDUTZ1RsBWh5X
1jBLLMnZ+KV2iATxoChh+/jJFqe02caByMpF/A4CkWS2s5ZFEasuPrG99IYIw9Ji
hwTUHoOwXZYN6egvFLlN
=rPTG
-----END PGP MESSAGE-----
</textarea>

<script>
var asyncTestCase = goog.testing.AsyncTestCase.createAndInstall(document.title);
asyncTestCase.stepTimeout = 20000;

var publicKeyAscii = document.getElementById('publicKeyAscii').value;
var publicKeyAsciiOther = document.getElementById('publicKeyAsciiOther').value;
var privateKeyAscii = document.getElementById('privateKeyAscii').value;

var USER_ID = 'RSARSA KEY (RSA/RSA key generated by gpg) <rsarsa@pgpkey.example.com>';
var KEY_PW = 'test';
var passphraseCallback = function(m) {
  return e2e.async.Result.toResult(KEY_PW);
};
var context = null;
var mockControl_;

function setUp() {
  context = new e2e.openpgp.ContextImpl(
      new goog.testing.storage.FakeMechanism());
  e2e.async.Result.getValue(context.initializeKeyRing(''));
  mockControl_ = new goog.testing.MockControl();
}


function tearDown() {
  mockControl_.$tearDown();
}


function testMojibakeInternally() {
  var mojibake = '文字化け';
  var krakozyabry = 'кракозя́бры';
  var floodResistantMirrorDrillingMachine = 'Árvíztűrő tükörfúrógép';
  var luanman = '乱码';
  var enc;
  asyncTestCase.waitForAsync('waiting for importKey.');
  context.importKey(fail, publicKeyAscii).then(function() {
    return context.importKey(passphraseCallback, privateKeyAscii);
  }, fail).then(function() {
    syncThisCall = e2e.async.Result.getValue(
        context.setArmorHeader('Comment', 'маймуница'));
    asyncTestCase.waitForAsync('waiting for encryptSign1.');
    return context.encryptSign(
      mojibake, // plaintext
      {}, // options
      [], // encrypt keys
      [floodResistantMirrorDrillingMachine, krakozyabry] // passphrases
    );
  }, fail).then(function(e) {
    enc = e;
    asyncTestCase.waitForAsync('waiting for verifyDecrypt1.');
    assertContains('Comment: маймуница', enc);
    return context.verifyDecrypt(
      function(uid) {
        return e2e.async.Result.toResult(floodResistantMirrorDrillingMachine);
      }, enc);
  }, fail).then(function(res) {
    assertEquals(mojibake, e2e.byteArrayToString(res.decrypt.data));
    return enc;
  }, fail).then(function(enc) {
    asyncTestCase.waitForAsync('waiting for veriDecrypt2.');
    return context.verifyDecrypt(function(uid) {
      return e2e.async.Result.toResult(krakozyabry);
    }, enc);
  }, fail).then(function(res) {
    asyncTestCase.waitForAsync('waiting for searchPrivateKey1.');
    assertEquals(mojibake, e2e.byteArrayToString(res.decrypt.data));
    return context.searchPrivateKey(USER_ID);
  }, fail).then(function(res) {
    asyncTestCase.waitForAsync('waiting for encryptSign2.');
    assert(res.length > 0);
    return context.encryptSign(
      luanman, {}, [], [], res[0]);
  }, fail).then(function(res) {
    asyncTestCase.waitForAsync('waiting for verifyClearSign.');
    assertContains(luanman, res);
    return context.verifyDecrypt(goog.abstractMethod, res);
  }, fail).then(function(res) {
    assert(res.verify.success.length > 0);
    // por fín!
    asyncTestCase.continueTesting();
  }, fail);
}

function encryptAsymmetric_(plaintext) {
  context.importKey(passphraseCallback, publicKeyAscii);
  var rsaKeys = e2e.async.Result.getValue(context.searchPublicKey(USER_ID));
  assertEquals(1, rsaKeys.length);

  context.importKey(passphraseCallback, publicKeyAsciiOther);
  var allKeys = new goog.structs.Map(e2e.async.Result.getValue(context.getAllKeys()));
  var keys = goog.array.flatten(allKeys.getValues());
  context.importKey(passphraseCallback, privateKeyAscii);
  asyncTestCase.waitForAsync('waiting for encryption.');
  context.encryptSign(
      plaintext,
      [], // Options.
      keys, // Keys to encrypt to.
      [], // Passphrases for symmetric ESKs.
      null // Keys to sign with.
  ).addCallback(function(encrypted) {
    asyncTestCase.waitForAsync('waiting for decryption.');
    context.verifyDecrypt(passphraseCallback, encrypted).addCallback(function(result) {
      asyncTestCase.continueTesting();
      var data = result['decrypt']['data'];
      assertEquals(plaintext, e2e.byteArrayToString(data));
    }).addErrback(function(error) {
      throw error;
    });
  }).addErrback(function(error) {
      throw error;
  });
}


function testEncrypt() {
  var plaintext = 'hello world from context message';
  encryptAsymmetric_(plaintext);
}


// Test for regression of https://github.com/google/closure-library/pull/356
// TODO(user): Chrome 48 takes longer to run this test, why?
function testEncryptLarge() {
  var plaintext = e2e.byteArrayToString(goog.array.repeat(3, 1000000));  // 1M
  encryptAsymmetric_(plaintext);
}


function testEncryptByteArray() {
  context.importKey(passphraseCallback, publicKeyAscii);
  var rsaKeys = e2e.async.Result.getValue(context.searchPublicKey(USER_ID));
  assertEquals(1, rsaKeys.length);

  context.importKey(passphraseCallback, publicKeyAsciiOther);
  var allKeys = new goog.structs.Map(e2e.async.Result.getValue(context.getAllKeys()));
  var keys = goog.array.flatten(allKeys.getValues());
  var plaintext = e2e.stringToByteArray('hello world from context message');
  context.importKey(passphraseCallback, privateKeyAscii);
  asyncTestCase.waitForAsync('waiting for encryption.');
  context.encryptSign(
      plaintext,
      [], // Options.
      keys, // Keys to encrypt to.
      [], // Passphrases for symmetric ESKs.
      null // Keys to sign with.
  ).addCallback(function(encrypted) {
    asyncTestCase.waitForAsync('waiting for decryption.');
    context.verifyDecrypt(passphraseCallback, encrypted).addCallback(function(result) {
      asyncTestCase.continueTesting();
      var data = result['decrypt']['data'];
      assertArrayEquals(plaintext, data);
    }).addErrback(function(error) {
      throw error;
    });
  }).addErrback(function(error) {
      throw error;
  });
}


function testEncryptSign() {
  var keys, privateKey, plaintext = 'hello world from signed message';
  asyncTestCase.waitForAsync('waiting for key import.');
  context.importKey(passphraseCallback, publicKeyAscii).then(function() {
    var allKeys = new goog.structs.Map(e2e.async.Result.getValue(context.getAllKeys()));
    keys = goog.array.flatten(allKeys.getValues());
    return context.importKey(passphraseCallback, privateKeyAscii);
  }, fail).then(function() {
    // Test signing.
    var privateKeys = e2e.async.Result.getValue(context.searchPrivateKey(USER_ID));
    privateKey = privateKeys[0]; // Only one key for signing.
    asyncTestCase.waitForAsync('waiting for encryption.');
    return context.encryptSign(
        plaintext,
        [], // Options.
        keys, // Keys to encrypt to.
        [], // Passphrases for symmetric ESKs.
        privateKey // Key to sign with.
      );
  }, fail).then(function(signedEncrypted) {
    asyncTestCase.waitForAsync('waiting for decryption.');
    return context.verifyDecrypt(passphraseCallback, signedEncrypted);
  }, fail).then(function(result) {
    var data = result['decrypt']['data'];
    var verify = result['verify'];
    assertEquals(plaintext, e2e.byteArrayToString(data));
    assertTrue(verify != null);
    assertEquals(0, verify.failure.length);
    assertEquals(1, verify.success.length);
    assertTrue(result['decrypt']['wasEncrypted']);
    assertArrayEquals(privateKey.key.fingerprint,
      verify.success[0].key.fingerprint);
    assertTrue(goog.array.contains(verify.success[0].uids, USER_ID));
    asyncTestCase.continueTesting();
  }, fail);
}


function testClearSign() {
  var plaintext = 'hello world from signed message';
  asyncTestCase.waitForAsync('waiting for public key import.');
  context.importKey(passphraseCallback, publicKeyAscii).then(function() {
    var rsaKeys = e2e.async.Result.getValue(context.searchPublicKey(USER_ID));
    assertEquals(1, rsaKeys.length);
    return context.importKey(passphraseCallback, publicKeyAsciiOther);
  }, fail).then(function() {
    var allKeys = new goog.structs.Map(e2e.async.Result.getValue(context.getAllKeys()));
    var keys = goog.array.flatten(allKeys.getValues());
    assertEquals(2, keys.length);
    asyncTestCase.waitForAsync('waiting for private key import.');
    return context.importKey(passphraseCallback, privateKeyAscii);
  }, fail).then(function() {
    // Test signing.
    var privateKeys = e2e.async.Result.getValue(context.searchPrivateKey(USER_ID));
    var privateKey = privateKeys[0]; // Only one key for signing.
    // Text clear signed.
    asyncTestCase.waitForAsync('waiting for clear sign.');
    return context.encryptSign(
        plaintext,
        [], // Options.
        [], // Keys to encrypt to.
        [], // Passphrases for symmetric ESKs.
        privateKey  // Key to sign with.
      )
  }, fail).then(function(clearSigned) {
    asyncTestCase.waitForAsync('waiting for clear sign verification.');
    return context.verifyDecrypt(goog.abstractMethod, clearSigned);
  }, fail).then(function(result) {
    assertEquals(plaintext, e2e.byteArrayToString(result.decrypt.data));
    assertEquals(0, result.verify.failure.length);
    assertEquals(1, result.verify.success.length);
    asyncTestCase.continueTesting();
  }, fail);
}

function testDecryptSymmetricBzip2() {
  var encrypted = document.getElementById('symmetricKeyBzip2').value;
  var plaintext = 'hello bz2 world\n';
  asyncTestCase.waitForAsync('waiting for decryption.');
  context.verifyDecrypt(passphraseCallback, encrypted).addCallback(function(result) {
    var data = result['decrypt']['data'];
    assertEquals(plaintext, e2e.byteArrayToString(data));
    asyncTestCase.continueTesting();
  }).addErrback(function(error) {
    throw error;
  });
}

function testEncryptSymmetric() {
  var plaintext = 'plaintext for symmetric';
  var res = context.encryptSign(
      plaintext,
      [], // Options.
      [], // Keys to encrypt to.
      ['someSecret', KEY_PW, 'anotherSecret']); // Passphrases for symmetric ESKs.
  var encrypted = e2e.async.Result.getValue(res);
  asyncTestCase.waitForAsync('waiting for decryption.');
  context.verifyDecrypt(passphraseCallback, encrypted).addCallback(function(result) {
    var data = result['decrypt']['data'];
    assertEquals(plaintext, e2e.byteArrayToString(data));
    asyncTestCase.continueTesting();
  }).addErrback(function(error) {
    throw error;
  });
}

function testEncryptToNoone() {
  var plaintext = 'plaintext';
  asyncTestCase.waitForAsync('waiting for encrypt.');
  context.encryptSign(
      plaintext,
      [], // Options.
      [], // Keys to encrypt to.
      [] // Passphrases for symmetric ESKs.
  ).addCallback(function(res) {
    fail("Encryption to noone should throw error");
  }).addErrback(function(error) {
    assertTrue(error instanceof e2e.openpgp.error.InvalidArgumentsError);
    asyncTestCase.continueTesting();
  });
}


// Test that if a passphrase fails to decrypt any ESKs, we ask for a new passphrase.
function testEncryptSymmetricRetry() {
  var passwordCount = 0;
  var correctPasswordNum = 2;
  var passphraseCallbackInc = function(message) {
    passwordCount = passwordCount + 1;
    assertTrue('Correct password was not recognized.', passwordCount <= correctPasswordNum);
    return e2e.async.Result.toResult(KEY_PW + passwordCount);
  };
  var contextInc = new e2e.openpgp.ContextImpl(
      new goog.testing.storage.FakeMechanism());
  e2e.async.Result.getValue(contextInc.initializeKeyRing(''));

  var plaintext = 'plaintext for symmetric';
  var res = contextInc.encryptSign(
      plaintext,
      [], // Options.
      [], // Keys to encrypt to.
      ['someSecret', KEY_PW + correctPasswordNum, 'anotherSecret']); // Passphrases for symmetric ESKs.
  var encrypted = e2e.async.Result.getValue(res);

  asyncTestCase.waitForAsync('waiting for decryption.');
  contextInc.verifyDecrypt(passphraseCallbackInc, encrypted).addCallback(function(result) {
    var data = result['decrypt']['data'];
    assertEquals(passwordCount, correctPasswordNum);
    assertEquals(plaintext, e2e.byteArrayToString(data));
    asyncTestCase.continueTesting();
  }).addErrback(fail);
}

function testVerifyClearSign() {
  var privateKey;
  var clearSigned;
  var plaintext = "cleartext message";

  asyncTestCase.waitForAsync('waiting for key import.');
  context.importKey(passphraseCallback, privateKeyAscii)
  .addCallback(function() {
    return context.importKey(passphraseCallback, publicKeyAscii);
  }).addCallback(function() {
    return context.searchPrivateKey(USER_ID);
  }).addCallback(function(privateKeys) {
    privateKey = privateKeys[0]; // Only one key for signing.
    asyncTestCase.waitForAsync('waiting for clear sign.');
    return context.encryptSign(
        plaintext,
        [], // Options.
        [], // Keys to encrypt to.
        [], // Passphrases for symmetric ESKs.
        privateKey  // Key to sign with.
    );
  }).addCallback(function(result) {
    clearSigned = result;
    asyncTestCase.waitForAsync('waiting for clear sign verification.');
    return context.verifyDecrypt(goog.abstractMethod, clearSigned);
  }).addCallback(function(result) {
    assertEquals(plaintext, e2e.byteArrayToString(result.decrypt.data));
    assertTrue(result.verify.success.length > 0);
    assertTrue(result.verify.failure.length == 0);
    assertFalse(result.decrypt.wasEncrypted);
    clearSigned = clearSigned.replace('cleartext', 'modified');
    asyncTestCase.waitForAsync('waiting for clear sign verification.');
    // Change the message.
    return context.verifyDecrypt(goog.abstractMethod, clearSigned);
  }).addCallbacks(function(result) {
    assertTrue(result.verify.failure.length > 0);
    assertTrue(result.verify.success.length == 0);
    asyncTestCase.continueTesting();
  }, fail);
}

function testDecryptNoKey() {
  context.importKey(passphraseCallback, publicKeyAscii);
  var keys = e2e.async.Result.getValue(context.searchPublicKey(USER_ID));
  assertEquals(1, keys.length);

  var plaintext = 'hello world from context message';
  var res = context.encryptSign(
      plaintext,
      [], // Options.
      keys, // Keys to encrypt to.
      [], // Passphrases for symmetric ESKs.
      null);  // Keys to sign with.
  var encrypted = e2e.async.Result.getValue(res);

  asyncTestCase.waitForAsync('waiting for decryption to fail.');
  context.verifyDecrypt(passphraseCallback, encrypted).addErrback(function(result) {
    asyncTestCase.continueTesting();
  });
}


function testParseErrorsDontAskForPassphrase() {
  var alreadyCalled = false;
  var passphraseCallbackOnce = function(message) {
    assertFalse('Correct password was not recognized.', alreadyCalled);
    alreadyCalled = true;
    return e2e.async.Result.toResult(KEY_PW);
  };
  var contextInc = new e2e.openpgp.ContextImpl(
      new goog.testing.storage.FakeMechanism());
  e2e.async.Result.getValue(contextInc.initializeKeyRing(''));

  var plaintext = 'plaintext for symmetric';
  var res = contextInc.encryptSign(
      plaintext,
      [], // Options.
      [], // Keys to encrypt to.
      [KEY_PW]); // Passphrases for symmetric ESKs.
  var encrypted = e2e.async.Result.getValue(res);

  asyncTestCase.waitForAsync('waiting for decryption.');

  var mock = mockControl_.createMethodMock(e2e.openpgp.block.LiteralMessage.prototype, 'getData');
  mock().$throws(new e2e.openpgp.error.ParseError('Simulating invalid literal package.'));

  mockControl_.$replayAll();

  contextInc.verifyDecrypt(passphraseCallbackOnce, encrypted).addCallback(function(result) {
    fail('e2e.openpgp.error.ParseError should be thrown');
  }).addErrback(function(error) {
    assertTrue(error instanceof e2e.openpgp.error.ParseError);
    asyncTestCase.continueTesting();
    mockControl_.$verifyAll();
  });
}


function testNestingLimitOnDecryption() {
  var contextInc = new e2e.openpgp.ContextImpl(
      new goog.testing.storage.FakeMechanism());
  e2e.async.Result.getValue(contextInc.initializeKeyRing(''));

  var plaintext = 'plaintext for symmetric';
  var res = contextInc.encryptSign(
      plaintext,
      [], // Options.
      [], // Keys to encrypt to.
      [KEY_PW]); // Passphrases for symmetric ESKs.
  var encrypted = e2e.async.Result.getValue(res);

  asyncTestCase.waitForAsync('waiting for decryption.');

  // simulate a compressed block quine - see CVE-2013-4402
  var mock = mockControl_.createMethodMock(e2e.openpgp.block.Compressed.prototype,
                                          'getMessage');
  mock().$times(contextInc.MAX_COMPRESSION_NESTING_LEVEL)
        .$returns(new e2e.openpgp.block.Compressed());
  mockControl_.$replayAll();
  contextInc.verifyDecrypt(passphraseCallback, encrypted).addCallback(function(result) {
    fail('e2e.openpgp.error.ParseError should be thrown');
  }).addErrback(function(error) {
    assertTrue(error instanceof e2e.openpgp.error.ParseError);
    asyncTestCase.continueTesting();
    mockControl_.$verifyAll();
  });
}


function testGenerateKey() {
  context.generateKey('ECDSA', 256, 'ECDH', 256, 'name', '', 'name@example.com', 1);
  var keys = new goog.structs.Map(e2e.async.Result.getValue(context.getAllKeys()));
  keys = goog.array.flatten(keys.getValues());
  assertEquals(2, keys.length);
  goog.array.forEach(keys, function(key) {
    goog.array.forEach(key.uids, function(uid) {
      assertEquals('name <name@example.com>', uid);
    });
  });
  assertEquals('ECDSA', keys[0].key.algorithm);
  assertEquals('ECDH', keys[1].subKeys[0].algorithm);
}

function testGenerateEmailOnlyKey() {
  context.generateKey('ECDSA', 256, 'ECDH', 256, '', '', 'name@example.com', 1);
  var keys = new goog.structs.Map(e2e.async.Result.getValue(context.getAllKeys()));
  keys = goog.array.flatten(keys.getValues());
  assertEquals(2, keys.length);
  goog.array.forEach(keys, function(key) {
    goog.array.forEach(key.uids, function(uid) {
      assertEquals('<name@example.com>', uid);
    });
  });
  assertEquals('ECDSA', keys[0].key.algorithm);
  assertEquals('ECDH', keys[1].subKeys[0].algorithm);
}

function testImportKeyCallsPassphraseOnce() {
  var called = false;
  asyncTestCase.waitForAsync('Waiting for import key.');
  context.importKey(function(uid) {
    if (called) fail('Import key called more than once.');
    called = true;
    return e2e.async.Result.toResult(KEY_PW);
  }, privateKeyAscii);
  asyncTestCase.timeout(function() {
    assertTrue(called);
    asyncTestCase.continueTesting();
  }, 500);
}


function testImportKeyMissingPassphrase() {
  var numCallsToPassphraseCb = 0;
  asyncTestCase.waitForAsync('Waiting for import key.');
  context.importKey(function(uid) {
    numCallsToPassphraseCb++;
    if (numCallsToPassphraseCb == 1) {
      asyncTestCase.waitForAsync('Waiting for second import key.');
      return e2e.async.Result.toResult();
    } else {
      return e2e.async.Result.toResult(KEY_PW);
    }
  }, privateKeyAscii);
  asyncTestCase.timeout(function() {
    assertEquals(2, numCallsToPassphraseCb);
    asyncTestCase.continueTesting();
  }, 1000);
}


function testImportKeyIncorrectPassphrase() {
  var numCallsToPassphraseCb = 0;
  asyncTestCase.waitForAsync('Waiting for import key.');
  context.importKey(function(uid) {
    numCallsToPassphraseCb++;
    if (numCallsToPassphraseCb == 1) {
      asyncTestCase.waitForAsync('Waiting for second import key.');
      return e2e.async.Result.toResult('foo');
    } else {
      return e2e.async.Result.toResult(KEY_PW);
    }
    return result;
  }, privateKeyAscii).addCallback(function() {
    assertEquals(2, numCallsToPassphraseCb);
    asyncTestCase.continueTesting();
  }).addErrback(fail);
}


function testImportPublicKeyCallbackCalled() {
  asyncTestCase.waitForAsync('Waiting for import public key.');
  context.importKey(passphraseCallback, privateKeyAscii).addCallback(function() {
    asyncTestCase.continueTesting();
  }).addErrback(fail);
}


function testImportSecretKeyCallbackCalled() {
  asyncTestCase.waitForAsync('Waiting for import private key.');
  context.importKey(passphraseCallback, publicKeyAscii).addCallback(function() {
    asyncTestCase.continueTesting();
  }).addErrback(fail);
}


function testImportsKeyUsesAllArmors() {
  asyncTestCase.waitForAsync('Waiting for import private key.');
  var bothKeys = publicKeyAscii + publicKeyAsciiOther;
  context.importKey(passphraseCallback, bothKeys).addCallback(function(uids) {
    assertEquals(2, uids.length);
    asyncTestCase.continueTesting();
  }).addErrback(fail);
}


function testSetPassphraseImportExportImportSecretKey() {
  // Set passphrase, import a key, export it, and try to import it again.
  var context = new e2e.openpgp.ContextImpl(
      new goog.testing.storage.FakeMechanism());
  var keyringExport;
  var counter = 0;
  var secondContext;
  context.initializeKeyRing('').
  addCallback(function() {
    // Encrypt the keyring.
    return context.changeKeyRingPassphrase(KEY_PW);
  }).addCallback(function() {
    asyncTestCase.waitForAsync('Waiting for first import of key.');
    return context.importKey(passphraseCallback, privateKeyAscii);
  }).addCallback(function() {
    return context.exportKeyring(true);
  }).addCallback(function(kr) {
    keyringExport = kr;
    // Create a new context with any passphrase.
    secondContext = new e2e.openpgp.ContextImpl(
        new goog.testing.storage.FakeMechanism());
    return secondContext.initializeKeyRing('');
  }).addCallback(function() {
    asyncTestCase.waitForAsync('Waiting for second import of key.');
    return secondContext.importKey(function(uid) {
      if (counter++ > 100) {
        fail('Failed to decrypt key.');
      }
      return e2e.async.Result.toResult(KEY_PW);
    }, keyringExport);
  }).addCallback(function(res) {
    assert(res.length > 0);
    assertEquals(1, counter);
    asyncTestCase.continueTesting();
  }).addErrback(fail);
}


function testSetPassphraseImportExportChangePassphraseImportSecretKey() {
  // Set passphrase, import a key, export it, change passphrase and try to import it again.
  var context = new e2e.openpgp.ContextImpl(
      new goog.testing.storage.FakeMechanism());
  var secondContext;
  var keyringExport;
  var counter = 0;
  context.initializeKeyRing('').
  addCallback(function() {
    // Encrypt the keyring.
    return context.changeKeyRingPassphrase(KEY_PW);
  }).addCallback(function() {
  asyncTestCase.waitForAsync('Waiting for first import of key.');
    return context.importKey(passphraseCallback, privateKeyAscii);
  }).addCallback(function() {
    return context.exportKeyring(true);
  }).addCallback(function(kr) {
    keyringExport = kr;
    secondContext = new e2e.openpgp.ContextImpl(
        new goog.testing.storage.FakeMechanism());
    asyncTestCase.waitForAsync('Waiting for password change.');
    return secondContext.initializeKeyRing('');
  }).addCallback(function() {
    return secondContext.changeKeyRingPassphrase('omg');
  }).addCallback(function() {
    asyncTestCase.waitForAsync('Waiting for second import of key.');
    return context.importKey(function(uid) {
      if (counter++ < 100) {
        return e2e.async.Result.toResult('bbq');
      } else {
        return e2e.async.Result.toResult('');
      }
    }, keyringExport);
  }).addCallback(function(res) {
    assertArrayEquals([], res);
    assertEquals(101, counter);
    asyncTestCase.continueTesting();
  });
}


function testGetKeyDescription() {
  var twoKeyBlocks = document.getElementById('twoKeyBlocks').value;
  asyncTestCase.waitForAsync('Waiting for first import of key.');
  var description = context.getKeyDescription(twoKeyBlocks).addCallback(function(
        description) {
      assertEquals(2, description.length);
      assertEquals('13ea2f602577fba20f428af1b2f39422e42c5ea7',
          goog.crypt.byteArrayToHex(description[0].key.fingerprint));
      assertEquals('13EA 2F60 2577 FBA2 0F42  8AF1 B2F3 9422 E42C 5EA7',
          description[0].key.fingerprintHex);
      assertEquals('ECDSA', description[0].key.algorithm);
      assertContains('<key1>', description[0].uids);
      assertEquals(false, description[0].key.secret);
      assertEquals('ECDH', description[0].subKeys[0].algorithm);
      assertContains('<key2>', description[1].uids);
      assertEquals('14B7 EF7A 0A02 074E 5C7F  50C7 537B 85C1 72B7 F7E2',
          description[1].key.fingerprintHex);
      assertEquals(false, description[1].key.secret);
      asyncTestCase.waitForAsync('waiting for getKeyDescription');
      context.getKeyDescription('invalid value').addCallback(function() {
        fail('Should result in error.');
      }).addErrback(function(e) {
        assertTrue(e instanceof e2e.openpgp.error.Error);
        asyncTestCase.continueTesting();
      });
  }).addErrback(fail);
}


function testSkipWeakKeys() {
  var keyring = document.getElementById('skipKeyBlocks').value;
  asyncTestCase.waitForAsync('Waiting for skipped key blocks.');
  var description = context.getKeyDescription(keyring).addCallback(function(
      description) {
    assertEquals(1, description.length);
    assertArrayEquals(['rsa-master'], description[0].uids);
    assertEquals(
        'bc763e9f85ef740d6190ab6cb721baa1b1d2e72b',
        goog.crypt.byteArrayToHex(description[0].key.fingerprint));
    asyncTestCase.continueTesting();
  }).addErrback(fail);
}
</script>
